package the_Crash;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;

import repast.simphony.random.RandomHelper;
import repast.simphony.space.continuous.ContinuousSpace;
import repast.simphony.space.grid.Grid;
import repast.simphony.space.grid.GridPoint;
import the_Crash.Stylists.SignalUnit;

public abstract class Signal extends BaseSimEntity {
	
	protected ArrayList<Tuple<MovementStates>> legalStates = new ArrayList<Tuple<MovementStates>>();
	protected Tuple<MovementStates> currentState;
	protected HashMap<MovementStates,Light> lights = new HashMap<MovementStates,Light>();
	protected int cycleLength;
	protected int cycleCount=0;
	
	public LinkedList<SignalUnit> TrafficSignal;
	
	public Signal(GridPoint location, Grid<Object> grid, ContinuousSpace<Object> space) {
		super(location, grid, space);		
		legalStates.add(new Tuple<MovementStates>(MovementStates.NorthStraight, MovementStates.SouthStraight));
		legalStates.add(new Tuple<MovementStates>(MovementStates.NorthTurn, MovementStates.SouthTurn));
		legalStates.add(new Tuple<MovementStates>(MovementStates.WestStraight, MovementStates.EastStraight));
		legalStates.add(new Tuple<MovementStates>(MovementStates.WestTurn, MovementStates.EastTurn));
		
		currentState = legalStates.get(RandomHelper.nextIntFromTo(0,legalStates.size()-1));
		createSignal(); // This creates the lights for the signal
	}
	
	public abstract boolean isAboutToChange();
	
	//updates the signal's light images
	public void updateLights()
	{
		for(MovementStates m: lights.keySet())
			lights.get(m).turnRed();
		lights.get(currentState.getFirst()).turnGreen();
		lights.get(currentState.getSecond()).turnGreen();
	}
	
	public HashMap<MovementStates,Light> getLights()
	{
		return lights;
	}
	
	public Tuple<MovementStates> getCurrentState()
	{	
		return currentState;
	}
	
	public String toString(){
		return "[" + this.currentState.getFirst().name() + ", " + this.currentState.getSecond().name() + "]";
	}
	
	//******************************************************************************
	/*
	 * This method allows a vehicle to ask if the light will allow it through the intersection based on where the car
	 * Wants to go after the intersection and which turn it wants to make (straight,left, or right)
	 */
	public boolean isOpen(Direction futureHeading, TravelDirection turn, Direction currentHeading){
		//we put the futureHeading and the turn to make into a single string and see if that state is open
		if(cycleCount <= 2){
			return false;
		}
		MovementStates turnToMake = this.getTurnToMake(futureHeading, turn, currentHeading);
		return this.currentState.getFirst().equals(turnToMake) || this.currentState.getSecond().equals(turnToMake);
	}
	
	//*****************************************************************************************
	//given heading information, determines what light corresponds
	protected MovementStates getTurnToMake(Direction futureHeading, TravelDirection turn, Direction currentHeading){
		String turnDirection = "Straight";
		Direction heading = futureHeading;
		if(turn.equals(TravelDirection.Left)){
			turnDirection = "Turn";
		} else if(turn.equals(TravelDirection.Right)){//need the direction that is open to be the same as the current direction
			heading = currentHeading;
		}
		MovementStates turnToMake = null;
		String turnName = heading.name() + turnDirection;
		for(MovementStates ms : MovementStates.values()){
			if(turnName.equals(ms.name())){
				turnToMake =  ms;
				break;
			}
		}
		return turnToMake;
	}
	
	//sets up the signal's light images
	private void createSignal() {
		TrafficSignal = new LinkedList<SignalUnit>();
		int points[] = {-1, 0, 1};
		int x= location.getX();
		int y = location.getY();
		MovementStates pStates[] = {MovementStates.WestTurn, MovementStates.EastStraight, MovementStates.NorthTurn, 
									MovementStates.NorthStraight, null, MovementStates.SouthStraight, 
									MovementStates.SouthTurn, MovementStates.WestStraight, MovementStates.EastTurn};
		for (int i = 0; i < points.length ; i++) {
			for (int j = 0; j < points.length; j++) {
				MovementStates mState = pStates[i * 3 + j];
				TrafficSignal.add(new SignalUnit(this,mState, new GridPoint(x + points[i], y + points[j]) , grid, space));	
			}
		}
	}
	
	public List<SignalUnit> GetLights() {
		return TrafficSignal;
	}
	
	public abstract void watchCar(int waitTime, Direction futureHeading, TravelDirection turn, Direction currentHeading);
	
	
}
